package cn.com.open.itembank.logic;

import cn.com.open.itembank.common.Function;
import cn.com.open.itembank.common.enums.DifficultyType;
import cn.com.open.itembank.dto.*;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by lucky on 2018/1/3.
 */
public class ComposePaperGeneticAlgorithm {

    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(ComposePaperGeneticAlgorithm.class);

    //region 适应度函数中各因素权重

    /**
     * 曝光度权重系数
     */
    private double weightDisplayrate = 3;
    /**
     * 难度权重系数
     */
    private double weightDifficulty = 1;
    /**
     * 题组中章节分布权重系数
     */
    private double weightChapterDistribute = 1;
    /**
     * 试卷中章分布权重系数
     */
    private double weightPaperChapterDistribute = 1;
    /**
     * 题组中知识点分布权重系数
     */
    private double weightKnowledgeDistribute = 0;
    /**
     * 试卷中知识点分布权重系数
     */
    private double weightPaperKnowledgeDistribute = 1;
    /**
     * 试卷中部分的音频权重系数
     */
    private double weightPartDuration = 3;

    //endregion

    //region 遗传算法相关参数

    /**
     * 当前最优适应度
     */
    private double curBestFitness = 0;
    /**
     * 同适应度累计次数
     */
    private int sameFitnessGenSum = 0;

    //endregion

    //region 试题/策略/试卷相关参数

    /**
     * 可用试题列表
     */
    private List<RuleQuestionModel> forChooseQuestionList;
    /**
     * 必选题列表
     */
    private List<RuleQuestionModel> mandatoryQuestionList;
    /**
     * 题型信息list
     */
    private List<QuestionTypeManageDto> questionTypes;
    /**
     * 当前策略内容
     */
    private RuleSaveModel curRuleModel;
    /**
     * 当前策略结构
     */
    private RuleStructure curRuleStructure;
    /**
     * 根据题型，类别列表获取的可用试题id列表
     */
    private Map<String, List<String>> questionIdListByTypeAndCategories;
    /**
     * 数据库内的全部互斥试题列表
     */
    private Map<String, List<String>> mutexQuestions;

    /**
     * 最终适应度
     */
    private double fitness = 0;

    //endregion

    //region 遗传算法基本参数

    /**
     * 种群大小
     */
    private int populationSize = 50;
    /**
     * 种群
     */
    private List<GenoType> population;
    /**
     * 当前代数
     */
    private int curGeneration;

    /**
     * 交叉概率
     */
    private static final double PXOVER = 0.5;
    /**
     * 交叉概率递减值（PXOVER - PXOVER_STEP_DIFF * MAX_GENS须大于0）
     */
    private static final double PXOVER_STEP_DIFF = 0.002;
    /**
     * 变异概率
     */
    private static final double PMUTATION = 0.25;
    /**
     * 变异概率递减值（PMUTATION - PMUTATION_STEP_DIFF * MAX_GENS须大于0）
     */
    private static final double PMUTATION_STEP_DIFF = 0.002;
    /**
     * 最大计算代数
     */
    private static final int MAX_GENS = 100;
    /**
     * 适应度相同情况下继续运算代数
     */
    private static final int MAX_CONTINUE_GENS = 30;
    /**
     * 格式化时间
     */
    private static final String LONG_DATA_FORMATTER = "yyyy/MM/dd-HH:mm:ss:SSS";

    //endregion

    /**
     * Gets fitness.
     *
     * @return the fitness
     */
    public double getFitness() {
        return fitness;
    }

    //region 构造函数

    /**
     * 构造函数.
     *
     * @param forChooseQuestionList             可选试题列表
     * @param mandatoryQuestionList             必选题列表
     * @param ruleSaveModel                     策略模型
     * @param questionIdListByTypeAndCategories [题型，可用试题id集合]列表
     * @param questionTypes                     the question types
     * @param mutexQuestions                    互斥试题列表
     */
    public ComposePaperGeneticAlgorithm(List<RuleQuestionModel> forChooseQuestionList, List<RuleQuestionModel> mandatoryQuestionList,
                                        RuleSaveModel ruleSaveModel, Map<String, List<String>> questionIdListByTypeAndCategories,
                                        List<QuestionTypeManageDto> questionTypes, Map<String, List<String>> mutexQuestions) {
        //要生成试卷的策略保存模型
        curRuleModel = ruleSaveModel;
        //可用试题的策略试题模型列表
        this.forChooseQuestionList = forChooseQuestionList;
        //必选题的策略试题模型列表
        this.mandatoryQuestionList = mandatoryQuestionList;
        //依据题型（大题）获取的在给定题型和类别下的可用试题id列表
        this.questionIdListByTypeAndCategories = questionIdListByTypeAndCategories;
        //题型list，用于生成试卷时候用
        this.questionTypes = questionTypes;
        //获取所有互斥试题信息
        this.mutexQuestions = mutexQuestions;

        this.initializeRuleStructure(curRuleModel);
    }
    //endregion

    //region 获取播放总时长

    /**
     * 获取播放总时长.
     *
     * @param duration       the duration
     * @param repeatTimes    the repeat times
     * @param repeatInterval the repeat interval
     * @return the int
     */
    private int getTotalDuration(int duration, int repeatTimes, int repeatInterval) {
        //媒体时长*（1+重播次数）+重播次数*重播间隔
        return duration * (1 + repeatTimes) + repeatTimes * repeatInterval;
    }

    /**
     * 获取试题占用总时长.
     *
     * @param mediaDuration    the media duration
     * @param repeatTimes      the repeat times
     * @param repeatInterval   the repeat interval
     * @param responseDuration the response duration
     * @return the int
     */
    private int getTotalDuration(int mediaDuration, int repeatTimes, int repeatInterval, int responseDuration) {
        return mediaDuration * (1 + repeatTimes) + repeatTimes * repeatInterval + responseDuration;
    }
    //endregion

    //region 设置难度系数列表

    /**
     * 设置难度系数列表.
     *
     * @param difficultyModel the difficulty model
     * @return the difficulty
     */
    private List<Double> setDifficulty(RuleDifficultyModel difficultyModel) {
        List<Double> difficulties = new ArrayList<>();

        Map<Float, Float> difficulty = Function.getDifficultyValue(DifficultyType.fromValue(difficultyModel.getType()));
        int minDifficultyint = 0;
        int maxDifficultyint = 1;

        for (Map.Entry<Float, Float> map : difficulty.entrySet()) {
            minDifficultyint = (int) (map.getKey() * 10);
            maxDifficultyint = (int) (map.getValue() * 10);
        }

        for (int i = minDifficultyint; i <= maxDifficultyint; i++) {
            difficulties.add((new BigDecimal(i)).divide(BigDecimal.valueOf(10f)).doubleValue());
        }

        return difficulties;
    }
    //endregion

    //region 初始化策略结构

    /**
     * 初始化策略结构.
     *
     * @param model the rule input model
     */
    private void initializeRuleStructure(RuleSaveModel model) {
        //策略结构
        curRuleStructure = new RuleStructure();
        boolean includeMediaPart = false;
        //小题组序号
        int curItemGroupSerialNum = 0;
        curRuleStructure.setFitnessFactorNum(0);
        //需要抽取的试题总数
        int needQuestionNum = 0;

        //模块（部分）
        for (RulePartSaveModel partModel : model.getParts()) {
            boolean isListening = partModel.isListening();
            RulePart rulePart = new RulePart(isListening ? 1 : 0);
            //包含音频听力试题
            if (isListening) {
                includeMediaPart = true;
            }

            int curPartDurationFixed = 0;
            if ((partModel.getInstruction() != null) && isListening) {
                for (InstructionModel ins : partModel.getInstruction()) {
                    curPartDurationFixed = curPartDurationFixed + getTotalDuration(ins.getDuration(), 0, 0);
                }
            }

            rulePart.getTimeLimits().setExpectedTime(partModel.getExpectedTime());
//            if (partModel.getMinTime() != null) {
//                rulePart.getTimeLimits().setMinTime(partModel.getMinTime());
//            }
//            if (partModel.getMaxTime() != null) {
//                rulePart.getTimeLimits().setMaxTime(partModel.getMaxTime());
//            }
            rulePart.getTimeLimits().setCheckTime(partModel.getCheckTime());

            if (isListening) {
                curPartDurationFixed = curPartDurationFixed + (int) rulePart.getTimeLimits().getCheckTime();
            } else {
                curPartDurationFixed = 0;
            }

            //大题
            for (RuleSectionSaveModel sectionModel : partModel.getSections()) {

                //获取答题说明时长
                if ((sectionModel.getInstruction() != null) && isListening) {
                    for (InstructionModel ins : sectionModel.getInstruction()) {
                        curPartDurationFixed = curPartDurationFixed + getTotalDuration(ins.getDuration(), 0, 0);
                    }
                }

                //初始化策略大题
                RuleSection ruleSection = new RuleSection();

                //region 初始化必考题
                if (sectionModel.getMustQuestion() != null) {
                    for (String mandatoryItem : sectionModel.getMustQuestion()) {
                        ruleSection.getMandatoryQuestions().add(mandatoryItem);
                    }
                }
                //endregion

                //region 初始化例题
                if (sectionModel.getExampleQuestion() != null) {
                    for (String exampleItem : sectionModel.getExampleQuestion()) {
                        ruleSection.getExampleQuestions().add(exampleItem);
                        //***************!!! 例题应该有答题时长，需要策略调用算法前准备好，目前暂时忽略************************
                        /**
                         * if (isListening) {
                         *     curPartDurationFixed = curPartDurationFixed + (int) getTotalDuration((double) exampleQuestion.MediaDuration, exampleItem.repeatTimes, exampleItem.repeatInterval, exampleItem.responseDuration);
                         * }
                         */
                    }
                }
                //endregion

                //region 初始化小题组

                for (RuleDifficultySaveModel difficultyModel : sectionModel.getDifficultyList()) {

                    curItemGroupSerialNum++;

                    RuleItemGroup ruleItemGroup = getRuleItemGroup(curItemGroupSerialNum, sectionModel.getQuestionTypeId(), difficultyModel);

                    if (ruleItemGroup.getItemCount() > 0) {
                        ruleSection.getRuleItemGroups().add(ruleItemGroup);
                    }

                    needQuestionNum = needQuestionNum + ruleItemGroup.getItemCount();
                }
                //endregion

                rulePart.getRuleSections().add(ruleSection);
            }
            rulePart.setDurationFixed(curPartDurationFixed);
            curRuleStructure.getRuleParts().add(rulePart);
        }

        if (weightPaperChapterDistribute != 0) {
            curRuleStructure.setFitnessFactorNum(curRuleStructure.getFitnessFactorNum() + 1);
        }
        if (weightPaperKnowledgeDistribute != 0) {
            curRuleStructure.setFitnessFactorNum(curRuleStructure.getFitnessFactorNum() + 1);
        }
        if (weightPartDuration != 0 && includeMediaPart) {
            curRuleStructure.setFitnessFactorNum(curRuleStructure.getFitnessFactorNum() + 1);
        }

        //修正种群数量
        if (needQuestionNum % 10 == 0) {
            populationSize = needQuestionNum;
        } else {
            populationSize = needQuestionNum - needQuestionNum % 10 + 10;
        }
    }

    /**
     * Gets rule item group.
     *
     * @param curItemGroupSerialNum the cur item group serial num
     * @param questionTypeId        the question type id
     * @param difficultyModel       the difficulty model
     * @return the rule item group
     */
    private RuleItemGroup getRuleItemGroup(int curItemGroupSerialNum, String questionTypeId, RuleDifficultySaveModel difficultyModel) {
        RuleItemGroup ruleItemGroup = new RuleItemGroup();
        ruleItemGroup.setItemCount(difficultyModel.getCount());
        ruleItemGroup.setSubItemCount(difficultyModel.getSubItemCount());
        ruleItemGroup.setQuestionCategories(difficultyModel.getCategory());
        ruleItemGroup.setMaxDuration(difficultyModel.getDurationEnd());
        ruleItemGroup.setMinDuration(difficultyModel.getDurationStart());
        ruleItemGroup.setRepeatInterval(difficultyModel.getRepeatInterval());
        ruleItemGroup.setRepeatTimes(difficultyModel.getRepeatTimes());
        ruleItemGroup.setResponseDuration(difficultyModel.getResponseDuration());
        ruleItemGroup.setSubjective(difficultyModel.isSubjective());
        ruleItemGroup.setSubjectiveSearchScope(difficultyModel.getSubjectiveSearchScope());
        ruleItemGroup.getQuestionCategoryRelationIDs().addAll(questionIdListByTypeAndCategories.get(questionTypeId));

        curRuleStructure.setFitnessFactorNum(curRuleStructure.getFitnessFactorNum() + 2);

        if (difficultyModel.getChapterList() != null) {
            curRuleStructure.setFitnessFactorNum(curRuleStructure.getFitnessFactorNum() + 1);

            for (String chapterID : difficultyModel.getChapterList()) {
                ruleItemGroup.getChapterIDs().add(chapterID);

                //加入到策略章范围中
                if (!curRuleStructure.getPaperChapterScopeIDs().contains(chapterID)) {
                    curRuleStructure.getPaperChapterScopeIDs().add(chapterID);
                }
            }
        }

        ruleItemGroup.setItemGroupSerialNum(curItemGroupSerialNum);
        ruleItemGroup.setQuestionTypeID(questionTypeId);

        ruleItemGroup.getDifficulties().addAll(setDifficulty(difficultyModel));

        return ruleItemGroup;
    }
    //endregion

    //region 进行组卷

    /**
     * 主函数，进行组卷.
     *
     * @return the map
     */
    public TestPaperModel composePaperGenetic() {

        if (populationSize > 0) {

            //region 形成完整试卷

            //记录生成完整试卷需要的代数

            //初始化完整试卷
            initialFirstPaperPopulation();

            if (population.size() <= populationSize)// 生成数量少 不符合条件就直接返回null  组卷失败
            {
                this.fitness = 0;
                return null;
            }

            //endregion

            //region 开始遗传算法

            curGeneration = 1;
            //保留第一代完整试卷中最好的试卷到Population位置
            keepBestOfFirstGeneratePaperGen();

            logger.info(String.format("保留第一代完整试卷中最好试卷完成,第一代最好适应度为 %f %n: 时间 %s",
                    population.get(populationSize).getFitness(), new Date()));

            //进行遗传算法
            while (curGeneration < MAX_GENS) {
                curGeneration++;

                //选择部分
                this.selectPart();

                //交叉部分
                this.crossover();

                //变异部分
                this.mutate();
                //精英保留策略（上一代最好的替换掉本代最差的，保留本代最好的）
                this.elitist();

                this.calculateSameFitnessGenNum();
                if (sameFitnessGenSum == MAX_CONTINUE_GENS) {
                    break;
                }
            }
            //endregion

            this.fitness = population.get(populationSize).getFitness();
        } else {
            this.fitness = -1;
        }

        return makeTestPaper();
    }
    //endregion

    //region 创建试卷

    /**
     * 创建试卷.
     *
     * @return the test paper model
     */
    private TestPaperModel makeTestPaper() {
        //curRuleModel
        TestPaperModel testPaper = new TestPaperModel();

        for (int partCounter = 0; partCounter < curRuleModel.getParts().size(); partCounter++) {
            RulePartSaveModel curPart = curRuleModel.getParts().get(partCounter);

            PaperPart paperPart = new PaperPart();

            //暂时没有实现：instructionCollection，TimeLimits

            for (int sectionCounter = 0; sectionCounter < curPart.getSections().size(); sectionCounter++) {
                RuleSectionSaveModel curSection = curPart.getSections().get(sectionCounter);

                // 从策略的 itemGroup 中获取题型标识
                if (curSection.getDifficultyList().isEmpty()) {
                    continue;
                }

                PaperSection paperSection = makePaperSection(curSection, partCounter, sectionCounter);

                paperPart.getSections().add(paperSection);
            }

            testPaper.getParts().add(paperPart);
        }

        logger.info(String.format("试卷内容完成: 时间 %s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        return testPaper;
    }

    //region 创建试卷大题

    /**
     * Gets question type.
     *
     * @param questionTypeId the question type id
     * @return the question type
     */
    private QuestionTypeManageDto getQuestionType(String questionTypeId) {
        QuestionTypeManageDto questionType = new QuestionTypeManageDto();
        for (QuestionTypeManageDto qt : questionTypes) {
            if (qt.getQuestionTypeID().equalsIgnoreCase(questionTypeId.toLowerCase())) {
                questionType = qt;
            }
        }

        return questionType;
    }

    /**
     * 创建试卷大题.
     *
     * @param curSection     the cur section
     * @param partCounter    the part counter
     * @param sectionCounter the section counter
     * @return the paper section
     */
    private PaperSection makePaperSection(RuleSectionSaveModel curSection, int partCounter, int sectionCounter) {
        PaperSection paperSection = new PaperSection();

        QuestionTypeManageDto questionType = getQuestionType(curSection.getQuestionTypeId());
        int questionInnerTypeId = questionType.getQuestionInnerTypeID();

        paperSection.setIdentifier(questionType.getSectionID());
        paperSection.setTitle(curSection.getSectionTitle());

        //暂未实现：instructionCollection,OptionalItemCount

        //例题
        if (!curSection.getExampleQuestion().isEmpty()) {
            for (String curItem : curSection.getExampleQuestion()) {
                PaperItem paperItem = makePaperItem(curItem, questionInnerTypeId, 0, 0, false);

                paperSection.getItems().add(paperItem);

                handleDisplayCount(curItem);
            }
        }

        int index = 0;
        //从最优的一代中将试题组合成试卷
        for (int itemGroupCounter = 0; itemGroupCounter < curSection.getDifficultyList().size(); itemGroupCounter++) {
            RuleDifficultySaveModel curItemGroup = curSection.getDifficultyList().get(itemGroupCounter);
            //score
            double itemScore = curItemGroup.getScore().divide(new BigDecimal(curItemGroup.getCount()), 2, BigDecimal.ROUND_HALF_UP).doubleValue();

            if (curItemGroup.getCount() > 0) {
                for (int t = 0; t < curItemGroup.getCount(); t++) {
                    QuestionStructure item = population.get(populationSize).getPaperPartDtos().get(partCounter)
                            .getPaperSectionDtos().get(sectionCounter)
                            .getPaperItemGroups().get(index).getQuestionStructures().get(t);

                    PaperItem paperItem = makePaperItem(item.getQuestionID(), questionInnerTypeId, item.getMediaDuration(),
                            itemScore, curItemGroup.isSubjective());

                    paperSection.getItems().add(paperItem);

                    handleDisplayCount(item.getQuestionID());
                }
                index++;
            }

            //必考题
            if (!curSection.getMustQuestion().isEmpty()) {
                for (String curItem : curSection.getMustQuestion()) {
                    for (RuleQuestionModel manatory : mandatoryQuestionList) {
                        if (manatory.getQuestionId().equalsIgnoreCase(curItem) &&
                                Function.getDifficultyType(manatory.getDifficulty()) == DifficultyType.fromValue(curItemGroup.getType())) {
                            PaperItem paperItem = makePaperItem(curItem, questionInnerTypeId, manatory.getMediaDuration(),
                                    itemScore, curItemGroup.isSubjective());

                            paperSection.getItems().add(paperItem);

                            handleDisplayCount(curItem);
                        }
                    }
                }
            }
        }

        return paperSection;
    }
    //endregion

    //region 创建试卷试题

    /**
     * 创建试卷试题.
     *
     * @param itemId         the item id
     * @param questionTypeId the question type id
     * @param duration       the duration
     * @param score          the score
     * @param isSubjective   the is subjective
     * @return the paper item
     */
    private PaperItem makePaperItem(String itemId, int questionTypeId, double duration, double score, boolean isSubjective) {
        PaperItem paperItem = new PaperItem();
        paperItem.setItemId(itemId);
        paperItem.setQuestionTypeId(questionTypeId);
        paperItem.setResponseDuration(duration);
        paperItem.setScore(score);
        paperItem.setSubjective(isSubjective);

        return paperItem;
    }
    //endregion

    //region 处理曝光次数

    /**
     * 处理曝光次数
     * （只更新本地信息，不记录数据库）
     *
     * @param itemId the item id
     */
    private void handleDisplayCount(String itemId) {
        for (RuleQuestionModel question : forChooseQuestionList) {
            if (question.getQuestionId().equalsIgnoreCase(itemId)) {
                question.setDisplayCount(question.getDisplayCount() + 1);
            }
        }
    }
    //endregion
    //endregion

    //region 初始化完整的第一代

    //region 得到题组试题筛选

    /**
     * 得到题组试题筛选.
     *
     * @param ruleItemGroup         the rule item group
     * @param curRulePart           the cur rule part
     * @return the question find
     */
    private List<RuleQuestionModel> getQuestionFind(RuleItemGroup ruleItemGroup, RulePart curRulePart) {
        List<RuleQuestionModel> curItemGroupForChooseQuestions = new ArrayList<>();

        for (RuleQuestionModel question : forChooseQuestionList) {
            if (question.getQuestionTypeId().equalsIgnoreCase(ruleItemGroup.getQuestionTypeID()) &&
                    (question.getMediaType() == curRulePart.getMediaType() || curRulePart.getMediaType() == 2) &&
                    ruleItemGroup.getSubItemCount() == question.getFieldCount() &&
                    ((question.getMediaDuration() >= (int) ruleItemGroup.getMinDuration() &&
                            question.getMediaDuration() <= (int) ruleItemGroup.getMaxDuration()) || question.getMediaType() == 0) &&
                    (ruleItemGroup.getChapterIDs().isEmpty() || ruleItemGroup.getChapterIDs().indexOf(question.getChapterId()) >= 0) &&
                    (ruleItemGroup.getDifficulties().isEmpty() || ruleItemGroup.getDifficulties().indexOf(question.getDifficulty()) >= 0) &&
                    ruleItemGroup.getQuestionCategoryRelationIDs().contains(question.getQuestionId())
                    ) {
                curItemGroupForChooseQuestions.add(question);
            }
        }

        return curItemGroupForChooseQuestions;
    }
    //endregion

    //region 从全部可用试题中选取可用试题

    /**
     * 从全部可用试题中选取可用试题.
     *
     * @param curItemGroupForChooseQuestions the cur item group for choose questions
     * @param genoType                       the geno type
     * @return the useful question
     */
    private List<RuleQuestionModel> getUsefulQuestion(List<RuleQuestionModel> curItemGroupForChooseQuestions, GenoType genoType) {
        List<RuleQuestionModel> curItemForChooseQuestions = new ArrayList<>();

        for (RuleQuestionModel question : curItemGroupForChooseQuestions) {
            if ((genoType.getSelectedQuestionSerialNums().isEmpty() ||
                    !genoType.getSelectedQuestionSerialNums().contains(Integer.valueOf(question.getSerialNumber())))
                    && (genoType.getMutexQuestionGroupIDs().isEmpty() ||
                    !genoType.getMutexQuestionGroupIDs().contains(question.getQuestionTypeId().toLowerCase()))) {
                curItemForChooseQuestions.add(question);
            }
        }

        return curItemForChooseQuestions;
    }
    //endregion

    //region 添加互斥试题

    /**
     * 添加互斥试题.
     *
     * @param genoType   the geno type
     * @param questionId the question id
     */
    private void addMutexQuestion(GenoType genoType, String questionId) {
        // 获取试题的互斥试题
        List<String> mutexQuqestionIDs = mutexQuestions.get(questionId);
        //将选择的题目的互斥题排除在以后的可选试题之外
        if (mutexQuqestionIDs != null && !mutexQuqestionIDs.isEmpty()) {
            for (String mutexQuqestionID : mutexQuqestionIDs) {
                if (!genoType.getMutexQuestionGroupIDs().contains(mutexQuqestionID)) {
                    genoType.getMutexQuestionGroupIDs().add(mutexQuqestionID);
                }
            }
        }

    }
    //endregion

    /**
     * 初始化完整的第一代.
     */
    private void initialFirstPaperPopulation() {
        boolean isValidate = true;
        int errCount = 0;// 错误次数，当错误10次 直接择放弃此次组卷， 以免死循环
        population = new ArrayList<>();
        GenoType genoType;

        while (population.size() <= populationSize) {
            genoType = new GenoType();

            //region 循环模块（部分）
            for (int partCounter = 0; partCounter < curRuleStructure.getRuleParts().size(); partCounter++) {
                RulePart curRulePart = curRuleStructure.getRuleParts().get(partCounter);
                PaperPartDto paperPartDto = new PaperPartDto(curRulePart.getMediaType());

                logger.info(String.format("####开始初始化第%d模块: 时间 %s", partCounter + 1, new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                //region 循环策略大题
                for (int sectionCounter = 0; sectionCounter < curRulePart.getRuleSections().size(); sectionCounter++) {
                    RuleSection curRuleSection = curRulePart.getRuleSections().get(sectionCounter);
                    PaperSectionDto paperSectionDto = new PaperSectionDto();

                    logger.info(String.format("####开始初始化第%d大题: 时间 %s", sectionCounter + 1, new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                    //region 将必考题添加到试卷大题的必考试题中
                    for (String mandatoryQuestion : curRuleSection.getMandatoryQuestions()) {
                        paperSectionDto.getMandatoryQuestions().add(mandatoryQuestion);
                        // 获取试题的互斥试题
                        addMutexQuestion(genoType, mandatoryQuestion);
                    }
                    //endregion

                    //region 将例题添加到试卷大题的必考试题中
                    for (String exampleQuestion : curRuleSection.getExampleQuestions()) {
                        paperSectionDto.getExampleQuestions().add(exampleQuestion);
                        // 获取试题的互斥试题
                        addMutexQuestion(genoType, exampleQuestion);
                    }
                    //endregion

                    logger.info(String.format("#####开始初始化小题组: 时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                    //region 添加题组
                    for (int itemGroupCounter = 0; itemGroupCounter < curRuleSection.getRuleItemGroups().size(); itemGroupCounter++) {
                        RuleItemGroup ruleItemGroup = curRuleSection.getRuleItemGroups().get(itemGroupCounter);

                        logger.info(String.format("######开始初始化第%d小题组: 时间：%s", ruleItemGroup.getItemGroupSerialNum(), new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                        PaperItemGroup paperItemGroup = new PaperItemGroup();
                        paperItemGroup.setItemGroupSerialNum(ruleItemGroup.getItemGroupSerialNum());
                        paperItemGroup.setQuestionTypeID(ruleItemGroup.getQuestionTypeID());

                        paperItemGroup.setRepeatInterval(ruleItemGroup.getRepeatInterval());
                        paperItemGroup.setRepeatTimes(ruleItemGroup.getRepeatTimes());
                        paperItemGroup.setResponseDuration(ruleItemGroup.getResponseDuration());
                        paperItemGroup.setSubjective(ruleItemGroup.isSubjective());

                        List<RuleQuestionModel> curItemGroupForChooseQuestions = getQuestionFind(ruleItemGroup, curRulePart);

                        logger.info(String.format("#######开始初始化小题: 时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));


                        for (int m = 0; m < ruleItemGroup.getItemCount(); m++) {

                            logger.info(String.format("初始化第%d小题: 时间：%s", m + 1, new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                            //从全部可用试题中选取可用试题
                            List<RuleQuestionModel> curItemForChooseQuestions = getUsefulQuestion(curItemGroupForChooseQuestions, genoType);

                            logger.info(String.format("查询第%d小题可用试题完成: 时间：%s", m + 1, new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                            //随机选取一道符合要求的试题
                            if (!curItemForChooseQuestions.isEmpty()) {
                                int pos = new Random().nextInt(curItemForChooseQuestions.size());
                                RuleQuestionModel selectedQuestion = curItemForChooseQuestions.get(pos);

                                genoType.getSelectedQuestionSerialNums().add(Integer.valueOf(selectedQuestion.getSerialNumber()));
                                // 获取试题的互斥试题
                                addMutexQuestion(genoType, selectedQuestion.getQuestionId());

                                QuestionStructure selectedQuestionStructure = new QuestionStructure();
                                selectedQuestionStructure.getByQuestion(selectedQuestion, mutexQuestions.get(selectedQuestion.getQuestionId()));
                                paperItemGroup.getQuestionStructures().add(selectedQuestionStructure);
                            } else {
                                isValidate = false;
                                errCount++;
                                // 试题不足 放弃此次
                            }
                            // 初始化小题
                        }

                        if (!paperItemGroup.getQuestionStructures().isEmpty()) {
                            paperSectionDto.getPaperItemGroups().add(paperItemGroup);
                        }
                    }
                    //endregion

                    paperPartDto.getPaperSectionDtos().add(paperSectionDto);

                }
                //endregion

                genoType.getPaperPartDtos().add(paperPartDto);
            }
            //endregion

            evaluateFitness(genoType);

            if (isValidate) {
                population.add(genoType);
            } else {
                if ((errCount >= 20 && population.isEmpty()) || errCount > 200) {
                    // 失败次数达到 20次 并且没有一个成功 基本可以视为组卷失败 直接放弃此次组卷
                    break;
                }
            }
        }

        GenoType lastGenoType = new GenoType();

        population.add(lastGenoType);
    }
    //endregion

    //region 计算个体的适应度

    /**
     * Geno type have same item boolean.
     *
     * @param genoType the geno type
     * @return the boolean
     */
    private boolean genoTypeHaveSameItem(GenoType genoType) {
        int i;
        int j;

        for (i = 0; i < genoType.getSelectedQuestionSerialNums().size() - 1; i++) {
            for (j = i + 1; j < genoType.getSelectedQuestionSerialNums().size(); j++) {
                if (genoType.getSelectedQuestionSerialNums().get(i).compareTo(genoType.getSelectedQuestionSerialNums().get(j)) == 0) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 计算个体的适应度.
     *
     * @param genoType the geno type
     */
    private void evaluateFitness(GenoType genoType) {
        if (genoTypeHaveSameItem(genoType)) {
            genoType.setFitness(-1);
            return;
        }

        //region 利用 区间 取值列表 计算适应度
        //适应度分数
        double score = 0;

        int mediaPartCount = 0;
        double mediaPartDurationDx = 0;

        logger.info(String.format("适应度计算开始，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        //region 循环模块（部分）
        for (int partCounter = 0; partCounter < genoType.getPaperPartDtos().size(); partCounter++) {
            PaperPartDto curPaperPartDto = genoType.getPaperPartDtos().get(partCounter);
            double partTotalDuration = 0;
            if (curPaperPartDto.getMediaType() == 1) {
                partTotalDuration = partTotalDuration + curRuleStructure.getRuleParts().get(partCounter).getDurationFixed();
            }

            //region 循环大题
            for (int sectionCounter = 0; sectionCounter < curPaperPartDto.getPaperSectionDtos().size(); sectionCounter++) {
                PaperSectionDto curPaperSectionDto = curPaperPartDto.getPaperSectionDtos().get(sectionCounter);
                //region 循环题组
                for (int itemGroupCounter = 0; itemGroupCounter < curPaperSectionDto.getPaperItemGroups().size(); itemGroupCounter++) {
                    PaperItemGroup curPaperItemGroup = curPaperSectionDto.getPaperItemGroups().get(itemGroupCounter);
                    RuleItemGroup curRuleItemGroup = curRuleStructure.getRuleParts().get(partCounter).getRuleSections().get(sectionCounter).getRuleItemGroups().get(itemGroupCounter);

                    for (QuestionStructure qs : curPaperItemGroup.getQuestionStructures()) {
                        curPaperItemGroup.getDifficulties().add(qs.getDifficulty());
                        curPaperItemGroup.getDisplayRates().add(qs.getDisplayCount());

                        if (weightChapterDistribute != 0 && !qs.getChapterID().isEmpty()) {
                            curPaperItemGroup.getChapterIDs().add(qs.getChapterID());
                        }

                        if (weightPaperChapterDistribute != 0 && !qs.getChapterID().isEmpty()) {
                            //找到当前章节Id对应的章Id
                            genoType.getPaperChapterIDs().add(qs.getChapterID());
                        }

                        if (curPaperPartDto.getMediaType() == 1) {
                            partTotalDuration = partTotalDuration + getTotalDuration(qs.getMediaDuration(), curPaperItemGroup.getRepeatTimes(),
                                    (int) curPaperItemGroup.getRepeatInterval(), (int) curPaperItemGroup.getResponseDuration());
                        }
                    }

                    logger.info(String.format("适应度计算--小题组初始化完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                    if (!curPaperItemGroup.getDisplayRates().isEmpty()) {
                        //曝光度
                        score = score + weightDisplayrate * evaluateDisplayRateScore(curPaperItemGroup.getDisplayRates());
                    }

                    //难度
                    if (!curPaperItemGroup.getDifficulties().isEmpty()) {
                        score = score + weightDifficulty * evaluateUniformDistributionScore(curRuleItemGroup.getDifficulties(),
                                curPaperItemGroup.getDifficulties());
                    }

                    //章节
                    if (weightChapterDistribute != 0 && !curPaperItemGroup.getQuestionStructures().isEmpty() &&
                            !curRuleItemGroup.getChapterIDs().isEmpty()) {
                        score = score + weightChapterDistribute * evaluateUniformDistributionScoreNew(curRuleItemGroup.getChapterIDs(),
                                curPaperItemGroup.getChapterIDs());
                    }

                    //知识点
                    logger.info(String.format("适应度计算--小题组计算完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

                }
                //endregion
            }
            //endregion

            if (curPaperPartDto.getMediaType() == 1) {
                mediaPartCount++;
                curPaperPartDto.setTotalDuration(partTotalDuration);
                    mediaPartDurationDx = mediaPartDurationDx +
                            Math.pow((Math.abs(partTotalDuration - curRuleStructure.getRuleParts().get(partCounter).getTimeLimits().getExpectedTime()) /
                                    (curRuleStructure.getRuleParts().get(partCounter).getTimeLimits().getMaxTime() -
                                            curRuleStructure.getRuleParts().get(partCounter).getTimeLimits().getMinTime())), 2);
            } else {
                curPaperPartDto.setTotalDuration(curRuleStructure.getRuleParts().get(partCounter).getDurationFixed());
            }
        }
        //endregion

        if (weightPaperChapterDistribute != 0 && !genoType.getPaperChapterIDs().isEmpty() && !curRuleStructure.getPaperChapterScopeIDs().isEmpty()) {
            score = score + weightPaperChapterDistribute * evaluateUniformDistributionScoreNew(curRuleStructure.getPaperChapterScopeIDs(),
                    genoType.getPaperChapterIDs());
        }

        if (mediaPartCount > 0 && weightPartDuration != 0) {
            score = score + weightPartDuration * (mediaPartDurationDx / mediaPartCount);
        }

        if (mediaPartCount == 0) {
            genoType.setFitness(1 - 2 * score / (curRuleStructure.getFitnessFactorNum() *
                    (weightDisplayrate + weightDifficulty + weightChapterDistribute + weightPaperChapterDistribute +
                            weightKnowledgeDistribute + weightPaperKnowledgeDistribute)));
        } else {
            genoType.setFitness(1 - 2 * score / (curRuleStructure.getFitnessFactorNum() *
                    (weightDisplayrate + weightDifficulty + weightChapterDistribute + weightPaperChapterDistribute +
                            weightKnowledgeDistribute + weightPaperKnowledgeDistribute + weightPartDuration)));
        }

        logger.info(String.format("适应度计算完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        //endregion
    }
    //endregion

    //region 计算曝光度系数

    /**
     * 计算曝光度系数.
     *
     * @param displayRates the display rates
     * @return the double
     */
    private double evaluateDisplayRateScore(List<Integer> displayRates) {
        // 如果没有增加一个默认值为0  解决 3个题组有题组没有选题的情况
        if (displayRates.isEmpty()) {
            displayRates.add(0);
        }

        double displayRateScore = 0.0;
        for (int displayRate : displayRates) {
            if (displayRate == 0) {
                displayRateScore = displayRateScore + 1;
            } else {
                displayRateScore = displayRateScore + 0.5 / (double) displayRate;//不能为1，否则displayRate为0时和1时没有差别
            }
        }
        displayRateScore = 1 - displayRateScore / (double) displayRates.size();

        return displayRateScore;
    }
    //endregion

    //region 计算均匀分布率

    /**
     * 计算均匀分布率.
     *
     * @param interval  区间
     * @param valueList 取值列表
     * @return 区间均匀分布率（越小越优）the double
     */
    private double evaluateUniformDistributionScore(List<Double> interval, List<Double> valueList) {
        //取值和
        int valueSum = 0;

        //取值在区间各点上出现次数列表
        List<Integer> emergeTimeList = new ArrayList<>();
        for (int i = 0; i < interval.size(); i++) {
            emergeTimeList.add(0);
        }

        //计算取值和 初始化次数列表
        for (int i = 0; i < valueList.size(); i++) {
            int intervalIndex = interval.indexOf(valueList.get(i));

            emergeTimeList.set(intervalIndex, emergeTimeList.get(intervalIndex) + 1);
            valueSum = valueSum + intervalIndex + 1;
        }

        //取值均值与区间均值之间的距离
        double avgDistence;
        if (interval.size() > 1) {
            avgDistence = Math.abs((double) (interval.size() + 1) / 2 - (double) valueSum / (double) valueList.size());
            avgDistence = avgDistence / Math.abs((double) (interval.size() - 1) / 2);
        } else {
            avgDistence = 1.0;
        }

        //离散系数转化后参数
        double discreteCoefficient = getDiscreteCoefficient(valueList.size(), interval.size(), emergeTimeList);

        return ((avgDistence + discreteCoefficient) / 2.0);
    }

    /**
     * Gets discrete coefficient.
     *
     * @param valueListSize  the value list size
     * @param intervalSize   the interval size
     * @param emergeTimeList the emerge time list
     * @return the discrete coefficient
     */
    private double getDiscreteCoefficient(int valueListSize, int intervalSize, List<Integer> emergeTimeList) {
        double discreteCoefficient = 0.0;

        if (valueListSize == 1) {
            discreteCoefficient = 1.0;
        } else {
            if (emergeTimeList.size() == 1) {
                discreteCoefficient = 1.0;
            } else {
                //次数列表均值
                double avgEmergeTime = (double) valueListSize / (double) intervalSize;
                for (int emergeTime : emergeTimeList) {
                    discreteCoefficient = discreteCoefficient + Math.pow(((double) emergeTime - avgEmergeTime), 2);
                }

                discreteCoefficient = discreteCoefficient / ((double) emergeTimeList.size() * (double) (emergeTimeList.size() - 1) * Math.pow(avgEmergeTime, 2));
            }
        }

        return discreteCoefficient;
    }


    /**
     * 计算均匀分布率.
     *
     * @param interval  区间
     * @param valueList 取值列表
     * @return 区间均匀分布率（越小越优）the double
     */
    private double evaluateUniformDistributionScoreNew(List<String> interval, List<String> valueList) {
        //取值和
        int valueSum = 0;
        //取值在区间各点上出现次数列表

        List<Integer> emergeTimeList = new ArrayList<>();
        for (int i = 0; i < interval.size(); i++) {
            emergeTimeList.add(0);
        }

        //计算取值和 初始化次数列表
        for (int i = 0; i < valueList.size(); i++) {
            int intervalIndex = interval.indexOf(valueList.get(i));

            //出现的第一个Section下的题组有章节，剩下的Section下无章节问题。
            if (intervalIndex > 0) {
                emergeTimeList.set(intervalIndex, emergeTimeList.get(intervalIndex) + 1);
                valueSum = valueSum + intervalIndex + 1;
            }
        }

        //取值均值与区间均值之间的距离
        double avgDistence;
        if (interval.size() > 1) {
            avgDistence = Math.abs((double) (interval.size() + 1) / 2 - (double) valueSum / (double) valueList.size());
            avgDistence = avgDistence / Math.abs((double) (interval.size() - 1) / 2);
        } else {
            avgDistence = 1.0;
        }

        //离散系数转化后参数
        double discreteCoefficient = getDiscreteCoefficient(valueList.size(), interval.size(), emergeTimeList);

        return ((avgDistence + discreteCoefficient) / 2.0);
    }
    //endregion

    //region 保留第一代试卷中最好的

    /**
     * 保留第一代试卷中最好的.
     */
    private void keepBestOfFirstGeneratePaperGen() {
        int curBestIndex = 0;
        double curbestfitness = 0;
        for (int i = 0; i < populationSize; i++) {
            if (population.get(i).getFitness() > curbestfitness) {
                curBestIndex = i;
                curbestfitness = population.get(i).getFitness();
            }
        }

        this.curBestFitness = curbestfitness;
        //记录最优个体

        population.set(populationSize, population.get(curBestIndex).copy());
    }
    //endregion


    //region 选择

    /**
     * 选择.
     */
    private void selectPart() {
        int i;
        double sumFitness = 0;

        //计算总适应度
        for (i = 0; i < populationSize; i++) {
            sumFitness += population.get(i).getFitness();
        }

        //计算相关适应度
        for (i = 0; i < populationSize; i++) {
            if (sumFitness > 0) {
                population.get(i).setRfitness(population.get(i).getFitness() / sumFitness);
            }
        }

        //计算累加适应度
        for (i = 0; i < populationSize; i++) {
            if (i == 0) {
                population.get(i).setCfitness(population.get(i).getRfitness());
            } else {
                population.get(i).setCfitness(population.get(i - 1).getCfitness() + population.get(i).getRfitness());

                if (population.get(i).getCfitness() > 1.0) {
                    population.get(i).setCfitness(1.0);
                }
            }
        }

        population = getNewPopulation();
    }

    /**
     * Gets new population.
     *
     * @return the new population
     */
    private List<GenoType> getNewPopulation() {
        List<GenoType> newPopulation = new ArrayList<>();

        double r = population.get(populationSize - 1).getCfitness() * 1000;

        for (int i = 0; i < populationSize; i++) {
            int randMaxValue = (int) r;
            if (randMaxValue <= 0) {
                randMaxValue = 1;
            }
            double p = new Random().nextInt(randMaxValue) / (double) 1000;

            if (p < population.get(0).getCfitness()) {
                newPopulation.add(population.get(0).copy());
            } else {
                for (int j = 0; j < populationSize - 1; j++) {
                    if (p >= population.get(j).getCfitness() && p < population.get(j + 1).getCfitness()) {
                        newPopulation.add(population.get(j + 1).copy());
                        break;
                    }
                }
            }
        }

        newPopulation.add(population.get(populationSize));

        return newPopulation;
    }
    //endregion

    //region 交叉

    /**
     * 交叉.
     */
    private void crossover() {
        int one = 0;
        int first = 0;
        double x;

        for (int i = 0; i < populationSize; ++i) {
            x = new Random().nextInt(1000) / 1000.0;
            if (x < (PXOVER - (MAX_GENS - curGeneration) * PXOVER_STEP_DIFF))//动态调整交叉概率
            {
                ++first;
                if (first % 2 == 0) {
                    xOver(one, i);
                } else {
                    one = i;
                }
            }
        }
    }

    //region 交叉两个个体

    /**
     * 交叉两个个体.
     *
     * @param index1 个体1下标
     * @param index2 个体2下标
     */
    private void xOver(int index1, int index2) {
        if (population.get(index1).getFitness() == Double.NaN) {
            population.get(index1).setFitness(0);
        }
        if (population.get(index2).getFitness() == Double.NaN) {
            population.get(index2).setFitness(0);
        }

        GenoType firstChildGeno = population.get(index1).copy();
        GenoType secondChildGeno = population.get(index2).copy();

        //产生随机模块序号
        int randomPartIndex = new Random().nextInt(curRuleStructure.getRuleParts().size());
        //模块中大题数目
        int sectionCountOfRandomPart = curRuleStructure.getRuleParts().get(randomPartIndex).getRuleSections().size();
        if (sectionCountOfRandomPart == 0) {
            return;
        }

        //产生随机大题序号
        int randomSectionIndex = new Random().nextInt(sectionCountOfRandomPart);
        //大题中小题组数目
        int itemGroupCountOfRandomSection = curRuleStructure.getRuleParts().get(randomPartIndex).getRuleSections().get(randomSectionIndex)
                .getRuleItemGroups().size();

        if (itemGroupCountOfRandomSection == 0) {
            return;
        }

        //产生随机小题组序号  重题
        int randomItemGroupIndex = new Random().nextInt(itemGroupCountOfRandomSection);
        //当前大题下某小题组下 个体所含试题数目
        int itemCountOfRandomItemGroup = firstChildGeno.getPaperPartDtos().get(randomPartIndex).getPaperSectionDtos().get(randomSectionIndex)
                .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().size();
        //产生随机小题位置
        int randomItemIndex = new Random().nextInt(itemCountOfRandomItemGroup);

        PaperItemGroup curPaperItemGroup1 = population.get(index1).getPaperPartDtos().get(randomPartIndex)
                .getPaperSectionDtos().get(randomSectionIndex).getPaperItemGroups().get(randomItemGroupIndex);
        PaperItemGroup curPaperItemGroup2 = population.get(index2).getPaperPartDtos().get(randomPartIndex)
                .getPaperSectionDtos().get(randomSectionIndex).getPaperItemGroups().get(randomItemGroupIndex);

        //region 方法1:先判断是否能交叉再交叉

        //先构造互斥题组ID列表以及去除原来选择的试题编号
        for (int i = 0; i < randomItemIndex + 1; i++) {
            clearAndRemakeMutexQuqestion(curPaperItemGroup1.getQuestionStructures().get(i).getMutexGroupIDs(),
                    firstChildGeno, secondChildGeno);

            clearAndRemakeMutexQuqestion(curPaperItemGroup2.getQuestionStructures().get(i).getMutexGroupIDs(),
                    secondChildGeno, firstChildGeno);
        }

        logger.info(String.format("构造互斥题组完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        //判断是否存在互换后有互斥问题
        boolean set1CanReplaceSet2 = true;
        boolean set2CanReplaceSet1 = true;

        int secondChildGenoSize = secondChildGeno.getMutexQuestionGroupIDs().size();
        for (int i = 0; i < secondChildGenoSize - 1; i++) {
            String id = secondChildGeno.getMutexQuestionGroupIDs().get(i);
            if (secondChildGeno.getMutexQuestionGroupIDs().subList(i + 1, secondChildGenoSize - 1).indexOf(id) != -1) {
                set1CanReplaceSet2 = false;
                break;
            }
        }

        int firstChildGenoSize = firstChildGeno.getMutexQuestionGroupIDs().size();
        for (int i = 0; i < firstChildGenoSize - 1; i++) {
            String id = firstChildGeno.getMutexQuestionGroupIDs().get(i);
            if (firstChildGeno.getMutexQuestionGroupIDs().subList(i + 1, firstChildGenoSize - 1).indexOf(id) != -1) {
                set2CanReplaceSet1 = false;
                break;
            }
        }

        logger.info(String.format("判断是否存在交叉后互斥完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        //开始互换
        if (set1CanReplaceSet2) {
            set1CanReplaceSet2 = xOverItem(randomPartIndex, randomSectionIndex, randomItemGroupIndex, randomItemIndex, secondChildGeno, curPaperItemGroup1);
        }

        if (set2CanReplaceSet1) {
            set2CanReplaceSet1 = xOverItem(randomPartIndex, randomSectionIndex, randomItemGroupIndex, randomItemIndex, firstChildGeno, curPaperItemGroup2);
        }

        logger.info(String.format("两组间交叉完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        List<GenoType> genoTypeList = new ArrayList<>();

        if (set1CanReplaceSet2) {
            evaluateFitness(secondChildGeno);
            genoTypeList.add(secondChildGeno);
        }

        if (set2CanReplaceSet1) {
            evaluateFitness(firstChildGeno);
            genoTypeList.add(firstChildGeno);
        }

        logger.info(String.format("适应度计算完毕，时间：%s", new SimpleDateFormat(LONG_DATA_FORMATTER).format(new Date())));

        if (genoTypeList.isEmpty()) {
            return;
        } else {
            genoTypeList.add(population.get(index1));
            genoTypeList.add(population.get(index2));
        }

        //endregion

        Collections.sort(genoTypeList, new Comparator<GenoType>() {
            @Override
            public int compare(GenoType arg0, GenoType arg1) {
                return Double.valueOf(arg0.getFitness()).compareTo(Double.valueOf(arg1.getFitness()));
            }
        });

        population.set(index1, genoTypeList.get(genoTypeList.size() - 1));
        population.set(index2, genoTypeList.get(genoTypeList.size() - 2));
    }
    //endregion

    /**
     * Clear and remake mutex quqestion.
     *
     * @param mutexGroupIDs the mutex group i ds
     * @param childGeno1    the child geno 1
     * @param childGeno2    the child geno 2
     */
    private void clearAndRemakeMutexQuqestion(List<String> mutexGroupIDs, GenoType childGeno1, GenoType childGeno2) {
        if (mutexGroupIDs != null && !mutexGroupIDs.isEmpty()) {

            //重新计算下第1个试卷的互斥列表
            childGeno1.getMutexQuestionGroupIDs().clear();
            childGeno1.getMutexQuestionGroupIDs().addAll(computeMutexQuqestion(childGeno1));

            //第2个试卷添加互斥列表 没有的增加
            if (!mutexGroupIDs.isEmpty()) {
                for (String mutexQuqestionID : mutexGroupIDs) {
                    if (!childGeno2.getMutexQuestionGroupIDs().contains(mutexQuqestionID.toLowerCase())) {
                        childGeno2.getMutexQuestionGroupIDs().add(mutexQuqestionID.toLowerCase());
                    }
                }
            }
        }
    }

    /**
     * X over item boolean.
     *
     * @param randomPartIndex      the random part index
     * @param randomSectionIndex   the random section index
     * @param randomItemGroupIndex the random item group index
     * @param randomItemIndex      the random item index
     * @param childGeno            the child geno
     * @param curPaperItemGroup    the cur paper item group
     * @return the boolean
     */
    private boolean xOverItem(int randomPartIndex, int randomSectionIndex, int randomItemGroupIndex, int randomItemIndex,
                              GenoType childGeno, PaperItemGroup curPaperItemGroup) {
        QuestionStructure sourceQS;
        QuestionStructure targetQS;
        boolean set1CanReplaceSet2 = true;

        for (int i = 0; i < randomItemIndex + 1; i++) {
            sourceQS = childGeno.getPaperPartDtos().get(randomPartIndex).getPaperSectionDtos().get(randomSectionIndex)
                    .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().get(i);
            targetQS = curPaperItemGroup.getQuestionStructures().get(i).copy();

            if (childGeno.getSelectedQuestionSerialNums().contains(Integer.valueOf(targetQS.getSerialNumber())))// 已经有这个试题
            {
                set1CanReplaceSet2 = false;
                break;
            }

            // 如果互斥试题组包含要更换进来的试题，并且互斥并不是因为要更换出去的试题， 那么更换进来必定互斥 ，不能更换
            if (childGeno.getMutexQuestionGroupIDs().contains(targetQS.getQuestionID()) &&
                    !targetQS.getMutexGroupIDs().contains(sourceQS.getQuestionID())) {
                set1CanReplaceSet2 = false;
                break;
            }

            childGeno.getSelectedQuestionSerialNums().add(Integer.valueOf(targetQS.getSerialNumber()));
            childGeno.getPaperPartDtos().get(randomPartIndex).getPaperSectionDtos().get(randomSectionIndex)
                    .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().set(i, targetQS);

        }

        return set1CanReplaceSet2;
    }

    //endregion

    //region 变异试题组

    //region 得到题组试题筛选

    /**
     * 得到题组未使用的试题筛选.
     *
     * @param ruleItemGroup         the rule item group
     * @param rulePart              the cur rule part
     * @param changedGenoType       the changed geno type
     * @return the question find
     */
    private List<RuleQuestionModel> getQuestionFindForUnUse(RuleItemGroup ruleItemGroup, RulePart rulePart,
                                                            GenoType changedGenoType) {
        List<RuleQuestionModel> curItemGroupForChooseQuestions = new ArrayList<>();

        for (RuleQuestionModel question : forChooseQuestionList) {
            if (question.getQuestionTypeId().equalsIgnoreCase(ruleItemGroup.getQuestionTypeID()) &&
                    question.getFieldCount() == ruleItemGroup.getSubItemCount() &&
                    question.getMediaType() == rulePart.getMediaType() &&
                    question.getMediaDuration() >= (int) ruleItemGroup.getMinDuration() &&
                    question.getMediaDuration() <= (int) ruleItemGroup.getMaxDuration() &&
                    (ruleItemGroup.getChapterIDs().isEmpty() || ruleItemGroup.getChapterIDs().indexOf(question.getChapterId()) >= 0) &&
                    (ruleItemGroup.getDifficulties().isEmpty() || ruleItemGroup.getDifficulties().indexOf(question.getDifficulty()) >= 0) &&
                    (changedGenoType.getSelectedQuestionSerialNums().isEmpty() ||
                            changedGenoType.getSelectedQuestionSerialNums().indexOf(Integer.valueOf(question.getSerialNumber())) < 0) &&
                    (changedGenoType.getMutexQuestionGroupIDs().isEmpty() ||
                            changedGenoType.getMutexQuestionGroupIDs().indexOf(question.getQuestionId().toLowerCase()) < 0) &&
                    ruleItemGroup.getQuestionCategoryRelationIDs().contains(question.getQuestionId())
                    ) {
                curItemGroupForChooseQuestions.add(question);
            }
        }

        return curItemGroupForChooseQuestions;
    }
    //endregion

    /**
     * 变异试题组.
     */
    private void mutate() {
        double x;

        for (int i = 0; i < populationSize; i++) {
            GenoType changedGenoType;

            x = new Random().nextInt(1000) / 1000.0;
            //动态调整变异概率
            if (x < (PMUTATION - (MAX_GENS - curGeneration) * PMUTATION_STEP_DIFF)) {
                //region 随机变异
                changedGenoType = population.get(i).copy();

                mutateGenoType(changedGenoType);

                if (changedGenoType.getFitness() > population.get(i).getFitness()) {
                    population.set(i, changedGenoType);
                }

                //endregion
            }
        }
    }

    /**
     * Mutate geno type.
     */
    private void mutateGenoType(GenoType changedGenoType) {
        //产生随机模块序号
        int randomPartIndex = new Random().nextInt(curRuleStructure.getRuleParts().size());
        //模块中大题数目
        int sectionCountOfRandomPart = curRuleStructure.getRuleParts().get(randomPartIndex).getRuleSections().size();
        if (sectionCountOfRandomPart == 0) {
            return;
        }

        //产生随机大题序号
        int randomSectionIndex = new Random().nextInt(sectionCountOfRandomPart);
        //大题中小题组数目
        int itemGroupCountOfRandomSection = curRuleStructure.getRuleParts().get(randomPartIndex)
                .getRuleSections().get(randomSectionIndex).getRuleItemGroups().size();

        if (itemGroupCountOfRandomSection == 0) {
            return;
        }

        //产生随机小题组序号 （错误 产生重题）
        int randomItemGroupIndex = new Random().nextInt(itemGroupCountOfRandomSection);
        //当前大题下某小题组
        RuleItemGroup ruleItemGroup = curRuleStructure.getRuleParts().get(randomPartIndex).getRuleSections().get(randomSectionIndex)
                .getRuleItemGroups().get(randomItemGroupIndex);

        //产生随机小题位置
        int randomItemIndex = new Random().nextInt(ruleItemGroup.getItemCount());

        QuestionStructure resourceQustionStructure = changedGenoType.getPaperPartDtos().get(randomPartIndex)
                .getPaperSectionDtos().get(randomSectionIndex)
                .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().get(randomItemIndex);

        if (!resourceQustionStructure.getMutexGroupIDs().isEmpty()) {
            changedGenoType.getMutexQuestionGroupIDs().removeAll(resourceQustionStructure.getMutexGroupIDs());
        }

        //查找题型匹配、章节匹配、难度匹配、本组未使用的试题
        List<RuleQuestionModel> curItemGroupForChooseQuestions = getQuestionFindForUnUse(ruleItemGroup,
                curRuleStructure.getRuleParts().get(randomPartIndex), changedGenoType);

        if (curItemGroupForChooseQuestions.isEmpty()) {
            return;
        }

        int pos = new Random().nextInt(curItemGroupForChooseQuestions.size());
        RuleQuestionModel selectedQuestion = curItemGroupForChooseQuestions.get(pos);

        QuestionStructure sourceQS;
        QuestionStructure targetQS = new QuestionStructure();

        targetQS.getByQuestion(selectedQuestion, mutexQuestions.get(selectedQuestion.getQuestionId()));
        sourceQS = changedGenoType.getPaperPartDtos().get(randomPartIndex).getPaperSectionDtos().get(randomSectionIndex)
                .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().get(randomItemIndex);
        changedGenoType.getSelectedQuestionSerialNums().remove(Integer.valueOf(sourceQS.getSerialNumber()));
        changedGenoType.getSelectedQuestionSerialNums().add(Integer.valueOf(targetQS.getSerialNumber()));

        // 互斥试题
        if (!targetQS.getMutexGroupIDs().isEmpty()) {
            for (String questionID : targetQS.getMutexGroupIDs()) {
                // 当试卷的互斥试题列表没有的时候添加 有的时候就不添加了
                if (changedGenoType.getMutexQuestionGroupIDs().contains(questionID.toLowerCase())) {
                    changedGenoType.getMutexQuestionGroupIDs().add(questionID.toLowerCase());
                }
            }
        }

        changedGenoType.getPaperPartDtos().get(randomPartIndex).getPaperSectionDtos().get(randomSectionIndex)
                .getPaperItemGroups().get(randomItemGroupIndex).getQuestionStructures().set(randomItemIndex, targetQS);

        evaluateFitness(changedGenoType);
    }
    //endregion

    //region 精英保留策略

    /**
     * 精英保留策略（上一代最好的替换掉本代最差的，保留本代最好的）.
     */
    private void elitist() {
        int i;
        double bestFitness;
        double worstFitness;             /* best and worst fitness values */
        int bestIndex = 0;
        int worstIndex = 0; /* indexes of the best and worst member */

        //找到本代中最优的和最差的下标
        bestFitness = population.get(0).getFitness();
        worstFitness = population.get(0).getFitness();

        for (i = 0; i < populationSize - 1; ++i) {
            if (population.get(i).getFitness() > population.get(i + 1).getFitness()) {
                if (population.get(i).getFitness() >= bestFitness) {
                    bestFitness = population.get(i).getFitness();
                    bestIndex = i;
                }
                if (population.get(i + 1).getFitness() <= worstFitness) {
                    worstFitness = population.get(i + 1).getFitness();
                    worstIndex = i + 1;
                }
            } else {
                if (population.get(i + 1).getFitness() >= bestFitness) {
                    bestFitness = population.get(i + 1).getFitness();
                    bestIndex = i + 1;
                }
                if (population.get(i).getFitness() <= worstFitness) {
                    worstFitness = population.get(i).getFitness();
                    worstIndex = i;
                }
            }
        }

        //进行替换操作
        if (worstFitness < population.get(populationSize).getFitness()) {
            population.set(worstIndex, population.get(populationSize).copy());
        }

        if (bestFitness >= population.get(populationSize).getFitness()) {
            population.set(populationSize, population.get(bestIndex).copy());
        }
    }
    //endregion

    //region 计算相同最优适应度出现的代数

    /**
     * 计算相同最优适应度出现的代数.
     */
    private void calculateSameFitnessGenNum() {
        if (population.get(populationSize).getFitness() == curBestFitness) {
            sameFitnessGenSum++;
        } else {
            curBestFitness = population.get(populationSize).getFitness();
            sameFitnessGenSum = 0;
        }
    }
    //endregion

    //region 计算当前试卷的互斥试题

    /**
     * 计算当前试卷的互斥试题.
     *
     * @param genoType the geno type
     * @return the list
     */
    private List<String> computeMutexQuqestion(GenoType genoType) {
        List<String> mutexQuqestions = new ArrayList<>();

        for (PaperPartDto pat : genoType.getPaperPartDtos()) {
            for (PaperSectionDto section : pat.getPaperSectionDtos()) {
                mutexQuqestions.addAll(computeMutexQuqestionEachSection(section));
            }
        }

        return mutexQuqestions;
    }

    /**
     * 在每个大题内计算当前试卷的互斥试题.
     *
     * @param section the section
     * @return the list
     */
    private List<String> computeMutexQuqestionEachSection(PaperSectionDto section) {
        List<String> mutexQuqestions = new ArrayList<>();

        for (PaperItemGroup group : section.getPaperItemGroups()) {
            for (QuestionStructure question : group.getQuestionStructures()) {
                if (!mutexQuqestions.contains(question.getQuestionID().toLowerCase())) {
                    mutexQuqestions.add(question.getQuestionID().toLowerCase());
                }
            }
        }

        // 必选题
        for (String question : section.getMandatoryQuestions()) {
            if (!mutexQuqestions.contains(question.toLowerCase())) {
                mutexQuqestions.add(question.toLowerCase());
            }
        }

        //例题
        for (String question : section.getExampleQuestions()) {
            if (!mutexQuqestions.contains(question.toLowerCase())) {
                mutexQuqestions.add(question.toLowerCase());
            }
        }

        return mutexQuqestions;
    }
    //endregion
}
